/* HackerPort driver for Linux - PIC programmer
 * version 0.7.2 date 2007-05-04
 * part of the hacker port project - http[s]://stefan.schuermans.info/hackerport/
 * Copyright (C) 2003-2004 stefan <stefan@schuermans.info>
 * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "hexfile.h"

//read a hex file
int hexfile_read( char * filename, st_hexfile_mem * mems, unsigned int mem_cnt )
{
  FILE * file;
  char line[1024], * p_chr;
  unsigned int line_no, end, addr_u, n, addr, type, i, val, j;
  unsigned long address;

  printf( "reading hexfile \"%s\"...\n", filename );

  file = fopen( filename, "rt" );
  if( file == NULL )
  {
    printf( "  could not open \"%s\": %s\n", filename, strerror( errno ) );
    return -1;
  }

  line_no = 1;
  end = 0;
  addr_u = 0;
  while( ! feof( file ) && ! end )
  {
    //get next line
    fgets( line, sizeof( line ), file );
    if( strchr( line, '\n' ) == NULL )
    {
      printf( "  line %u too long\n", line_no );
      fclose( file );
      return -1;
    }

    //parse header of line
    if( sscanf( line, ":%2X%4X%2X", &n, &addr, &type ) != 3 )
    {
      printf( "  line %u has got invalid format\n", line_no );
      fclose( file );
      return -1;
    }

    switch( type )
    {
      //data
      case 0x00:
        p_chr = line + 9;
        for( i = 0; i < n; i++ )
        {
          //get byte
          if( sscanf( p_chr, "%2X", &val ) != 1 )
          {
            printf( "  invalid format of data (byte %u of %u) in line %u\n", i, n, line_no );
            fclose( file );
            return -1;
          }
          p_chr += 2;
          //calculate address
          address = ((unsigned long)addr_u << 16 | (unsigned long)addr) + (unsigned long)i;
          //locate address in memory regions
          for( j = 0; j < mem_cnt; j++ )
            if( address >= mems[j].addr && address < mems[j].addr + mems[j].size )
              break;
          if( j >= mem_cnt )
          {
            printf( "  invalid address 0x%08lX (line %u)\n", address, line_no );
            fclose( file );
            return -1;
          }
          //store byte
          mems[j].p_buffer[address - mems[j].addr] = (unsigned char)val;
        }
        break;

      //end of data
      case 0x01:
        if( n != 0 )
        {
          printf( "  invalid N (%d, exprected 0) for end marker in line %u\n", n, line_no );
          fclose( file );
          return -1;
        }
        end = 1;
        break;

      //upper address
      case 0x04:
        if( n != 2 )
        {
          printf( "  invalid N (%d, exprected 2) for upper address in line %u\n", n, line_no );
          fclose( file );
          return -1;
        }
        if( sscanf( line + 9, "%4X", &addr_u ) != 1 )
        {
          printf( "  invalid format for upper address in line %u\n", line_no );
          fclose( file );
          return -1;
        }
        break;

      //unknown type
      default:
        printf( "  unknown type 0x%02X in line %u\n", type, line_no );
        fclose( file );
        return -1;
    } //switch( type )

    line_no++;   
  } //while( ! feof( file ) )

  fclose( file );

  return 0;
}
